use crate::todo::MAX_KLINE_DAYS;

use super::util::valley_klines::calculate_sma;
use super::util::valley_klines::find_crossover_indices;
use super::util::valley_klines::find_crossunder_indices;
use super::util::valley_klines::find_highest;
use super::util::valley_klines::find_valleys;
use super::StrategyPerformance;
use anyhow::Result;
use anyhow::anyhow;

use chrono::DateTime;
use chrono::Utc;
use fudata::db;

use fudata::model::kline::KlineValley;
use fudata::model::kline::Klt;
use fudata::model::kline::Kline;

use fudata::model::kline::Ohlcv;
use fudata::model::trade::trade_storder::PullbackSell;
use fudata::model::trade::trade_storder::ValleyReboundBuy;
use fudata::model::trade::trade_storder::TradeStorder;
use fudata::model::trade::trade_storder::TradeStorderData;
use fudata::round2;

use log::info;
use log::trace;
use serde::Deserialize;
use serde::Serialize;
use surrealdb::sql::Thing;

use fudata::model::trade::trade_storder::ValleyStopLossSell;

/// Bottom fishing: 这是最常用的表达方式之一，用来形容寻找并买入价格已经跌至底部的股票的行为。
///
/// 当前价格在最低10个价格内，且sma5 > sma21，买入,并用回落2%卖出保护，如果涨则调整回落卖出幅度为3%等
///
/// 抄底应该超涨停比较多活跃的股票，应该按照涨停次数来排列股票，应该人工标记阶段最低点K线来抄底
///
pub struct BottomFishingStrategy {}

pub struct BottomFishingStrategyWorker {
    security_id: String,
    /// all klines for finding lowest prices as basic println
    all_klines: Vec<Kline>,

    valleys: Vec<KlineValley>,

    /// close 均值5日下穿均值13日
    crossunders: Vec<(String, usize)>,
    /// close 均值5日上穿均值13日
    crossovers: Vec<(String, usize)>,

    sma5s: Vec<(String, f64)>,
    sma13s: Vec<(String, f64)>,

    current_valley: Option<KlineValley>,
    trade_orders: Vec<TradeStorder>,
    performances: Vec<StrategyPerformance>,
}

/// 处理缠论中Kline的包含关系
fn handle_inclusion(klines: Vec<Kline>) -> Vec<Kline> {
    let mut result: Vec<Kline> = Vec::new();
    let mut iter = klines.into_iter().peekable();
    let mut // If this is the first Kline, assume it's up
    is_up = true;

    while let Some(current_kline) = iter.next() {
        // Determine the direction based on the previous non-included Kline
        if let Some(ref mut prev_kline) = result.last_mut() {
            if current_kline.high >= prev_kline.high {
                is_up = true; // Up trend
            } else if current_kline.low <= prev_kline.low {
                is_up = false; // Down trend
            }

            if
                (current_kline.high >= prev_kline.high && current_kline.low <= prev_kline.low) ||
                (prev_kline.high >= current_kline.high && prev_kline.low <= current_kline.low)
            {
                // Update the current_kline based on the direction

                if is_up {
                    // If previous Kline was "Up", use max high and max low
                    prev_kline.high = current_kline.high.max(prev_kline.high);
                    prev_kline.low = current_kline.low.max(prev_kline.low);
                } else {
                    // If previous Kline was "Down", use min high and min low
                    prev_kline.high = current_kline.high.min(prev_kline.high);
                    prev_kline.low = current_kline.low.min(prev_kline.low);
                }
            } else {
                // Add the current_kline to the result with its direction
                result.push(current_kline.clone());
            }
        } else {
            // If this is the first Kline, add it.
            result.push(current_kline.clone());
        }
    }

    result
}

pub struct BottomFractal {
    pub left: Kline,
    pub bottom: Kline,
    pub right: Kline,
    /// right/left ratio
    pub rlratio: i16,
    pub dayreverse: bool,
    pub topleft: String,
    /// bottom/topleft ratio
    pub b2tratio: i16,
}

/// "In缠论, a Bottom Fractal (底分型) is a specific pattern of three consecutive K-lines where the middle K-line has the lowest low,
/// indicating a potential reversal from a downtrend to an uptrend."
///
fn find_bottom_fractal(
    all_klines: &Vec<Kline>,
    crossovers: &Vec<(String, usize)>,
    crossunders: &Vec<(String, usize)>,
    limit_last_num: i8
) -> Vec<BottomFractal> {
    let mut bottom_klines = Vec::new();
    let len = all_klines.len();

    // Ensure there are at least three K-lines to form a bottom fractal
    if len < 3 {
        return bottom_klines;
    }

    // Iterate up to the second-to-last K-line to ensure we have three consecutive K-lines
    for i in 1..len - 2 {
        let left_kline = &all_klines[i - 1];
        let bottom_kline = &all_klines[i];
        let right_kline = &all_klines[i + 1];

        // Check if the current K-line is the lowest low among the three
        if bottom_kline.low <= left_kline.low && bottom_kline.low <= right_kline.low {
            // Additional check to ensure it's a valid bottom fractal in缠论
            if left_kline.high > bottom_kline.high && right_kline.high > bottom_kline.high {
                let left_highest = find_down_left_highest(all_klines, crossovers, crossunders, i);
                let drop = if let Some(ref lh) = left_highest {
                    (
                        lh.0.clone(),
                        (((all_klines[lh.1].high - bottom_kline.high) / bottom_kline.high) *
                            100.0) as i16,
                    )
                } else {
                    ("".to_string(), 0 as i16)
                };

                let right_left_ratio = (((right_kline.high - left_kline.high) / left_kline.high) *
                    100.0) as i16;
                bottom_klines.push(BottomFractal {
                    left: left_kline.clone(),
                    bottom: bottom_kline.clone(),
                    right: right_kline.clone(),
                    rlratio: right_left_ratio,
                    dayreverse: bottom_kline.is_day_reverse(3.0, 4.0),
                    topleft: drop.0,
                    b2tratio: drop.1,
                });
            }
        }
    }

    // Sort the bottom_klines by index in descending order to get the latest ones
    bottom_klines.sort_by(|a, b| b.bottom.klts.cmp(&a.bottom.klts));

    // Limit the number of bottom fractals to the latest `limit_last_num`
    bottom_klines.truncate(limit_last_num as usize);

    // Optionally, sort back to ascending order if you want them in original order
    bottom_klines.sort_by(|a, b| a.bottom.klts.cmp(&b.bottom.klts));

    bottom_klines
}

///
/// 查找当前Kline的最近左侧下跌最高Kline
///
pub fn find_down_left_highest(
    all_klines: &Vec<Kline>,
    crossovers: &Vec<(String, usize)>,
    crossunders: &Vec<(String, usize)>,
    kline_idx: usize
) -> Option<(String, usize)> {
    let mut crossunder = None;
    let mut crossover = None;
    for under in crossunders.iter() {
        if under.1 < kline_idx {
            crossunder = Some(under);
        }
    }
    for over in crossovers.iter() {
        if over.1 < kline_idx {
            crossover = Some(over);
        }
    }

    if let (Some(over), Some(under)) = (crossover, crossunder) {
        // info!("over={:?} under={:?} kline_idx={:?}", over, under, kline_idx);
        if over.1 < under.1 {
            let left_highest = find_highest(all_klines, over.1, kline_idx);
            return left_highest;
        }
    }

    None
}

impl BottomFishingStrategyWorker {
    pub async fn new(security_id: &str, klt: &Klt) -> Result<Self> {
        let mut all_klines = db::kline::select(
            security_id,
            None,
            Some(klt),
            Some("klts DESC"),
            0,
            250 //MAX_KLINE_DAYS as u16
        ).await?;
        all_klines.reverse();

        // 处理缠论Kline包含关系
        // let all_klines = handle_inclusion(all_klines);

        let sma5s: Vec<(String, f64)> = calculate_sma(&all_klines, 5);
        for (idx, ov) in sma5s.iter().enumerate() {
            if idx < 3 || idx >= sma5s.len() - 3 {
                // info!("Sma5[{idx}] {:?}", ov);
            }
        }
        // info!("");
        let sma13s = calculate_sma(&all_klines, 13);
        for (idx, ov) in sma13s.iter().enumerate() {
            if idx < 3 || idx >= sma13s.len() - 3 {
                // info!("Sma21[{idx}] {:?}", ov);
            }
        }

        // info!("\ncrossovers*********");
        let crossovers = find_crossover_indices(&sma5s, &sma13s);
        // crossovers.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
        for (idx, ov) in crossovers.iter().enumerate() {
            // info!("Crossover[{idx}] {:?}", ov);
        }

        // info!("\ncrossunders*********");
        let crossunders = find_crossunder_indices(&sma5s, &sma13s);
        // crossunders.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));

        for (idx, ov) in crossunders.iter().enumerate() {
            // info!("Crossunders[{idx}] {:?}", ov);
        }

        // let valleys = find_valleys(&all_klines, 18.0, 28.0);
        let valleys: Vec<KlineValley> = Vec::new();
        return Ok(BottomFishingStrategyWorker {
            security_id: security_id.to_string(),
            all_klines: all_klines,
            valleys,
            current_valley: None,

            crossovers,
            crossunders,
            sma5s,
            sma13s,

            trade_orders: Vec::new(),
            performances: Vec::new(),
        });
    }

    fn test_run(&mut self, date_part: &str) {
        let mut kline0 = None;
        for kl in self.all_klines.iter() {
            if kl.id_date().eq_ignore_ascii_case(date_part) {
                kline0 = Some(kl);
                break;
            }
        }
        if let Some(kline) = kline0 {
            self.current_valley = self.get_current_valley(kline);

            println!("Found Valley {:?} \n", self.current_valley);

            // 反弹买入
            let rebound_buy = self.get_valley_rebound_buy(kline);

            println!("ReboundBuy {:?} ", rebound_buy);
        }
    }

    pub async fn run_buy_sell(&mut self) -> &Vec<TradeStorder> {
        // let mut trade_orders: Vec<TradeStorder> = Vec::new();
        for kline in self.all_klines.iter() {
            // 获取当前Kline最近支撑谷底
            self.current_valley = self.get_current_valley(kline);

            // 反弹买入
            if let Some(to) = self.get_valley_rebound_buy(kline) {
                self.trade_orders.push(to);
            }

            // 止盈或止损卖出
            if let Some(buy_sell) = self.get_stop_loss_sell(kline) {
                let perf = StrategyPerformance {
                    buyorder_id: buy_sell.0.id_raw(),
                    buy_price: buy_sell.0.st_price,
                    sellorder_id: buy_sell.1.id_raw(),
                    sell_price: buy_sell.1.st_price,
                    volume: 100,
                    message: "回撤卖出".to_owned(),
                };
                self.trade_orders.push(buy_sell.1);
                self.performances.push(perf);
            }

            // 更新移动止盈价
            if let Some(stop_price) = self.get_moving_stop_price(kline) {
                if let Some(to) = self.trade_orders.last_mut() {
                    match &mut to.data {
                        Some(TradeStorderData::ValleyReboundBuy(buy)) => {
                            info!(
                                " . --Upd@ ValleyReboundBuy by Kline:{} stop_price:{}",
                                kline.id_raw(),
                                stop_price
                            );
                            buy.stop_price = stop_price;
                        }
                        Some(TradeStorderData::PullbackBuy(buy)) => {
                            info!(
                                " . --Upd@ PullbackBuy by Kline:{} stop_price:{}",
                                kline.id_raw(),
                                stop_price
                            );
                            buy.stop_price = stop_price;
                        }
                        _ => {}
                    }
                }
            }

            // 更新Valley成为阻力位、支撑位
            for (idx, is_support) in self.get_resistance_support_valley_flags(kline).iter() {
                self.valleys[*idx].is_support = *is_support;
            }
        }

        &self.trade_orders
    }

    pub fn is_last_buy(&self) -> bool {
        match self.trade_orders.last() {
            Some(to) =>
                match to.data {
                    Some(TradeStorderData::ValleyReboundBuy(_)) => true,
                    Some(TradeStorderData::PullbackBuy(_)) => true,
                    _ => false,
                }
            None => false,
        }
    }

    // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
    // 0, SZ.STK.300363, 17.35, 68.85, 17104.91, -985.74, 448.20, 122
    // 1, SZ.STK.002648, 12.19, 77.03, 1502.70, -123.26, 46.77, 74
    // 2, SH.STK.603806, 10.47, 65.85, 4576.44, -437.16, 169.04, 82
    // 3, SH.STK.601555, 6.02, 61.54, 672.12, -111.74, 25.77, 26
    fn get_resistance_support_valley_flags(&self, kline: &Kline) -> Vec<(usize, bool)> {
        let mut result = Vec::new();
        for (idx, valley) in self.valleys.iter().enumerate() {
            if
                valley.lowest.low > kline.high && // 跌破波谷就成为阻力位
                kline.klts.signed_duration_since(valley.right_most.klts).num_days() > 0
            {
                if
                    valley.id_raw().eq_ignore_ascii_case("SZ.STK.300868.DAY.20220427") &&
                    valley.is_support
                {
                    trace!(
                        "[{idx}] Kline:{}({}-{})  set Valley:{}({}-{}) to false",
                        kline.id_raw(),
                        kline.low,
                        kline.high,
                        valley.lowest.id_raw(),
                        valley.lowest.low,
                        valley.lowest.high
                    );
                }
                result.push((idx, false)); // false 设为阻力位
            }
            //  else if
            //     valley.lowest.high * 1.2 < kline.low // 突破谷底又成为支撑位，又把它设为支撑位表现不好
            // {
            //     result.push((idx, true));
            // }
        }

        result
    }

    fn get_current_valley(&self, kline: &Kline) -> Option<KlineValley> {
        // self.valleys倒序排列，找到离Kline最近的Valley
        for valley in self.valleys.iter() {
            // we can no use future data, only previous valley can be used for late kline
            // 最近valley，并且当前Kline.low在波谷最低价~最高价之间
            if
                valley.is_support &&
                valley.right_most.klts < kline.klts &&
                valley.lowest.low < kline.low &&
                kline.low < valley.lowest.high
            {
                return Some(valley.clone());
            }
        }

        // 已存在current_valley并且当前Kline在谷底之上，就继续用
        if let Some(valley) = &self.current_valley {
            if kline.close > valley.lowest.low && valley.is_support {
                // use exist valley
                return Some(valley.clone());
            }
        }
        None
    }

    /// BUY@ conditions: 使用【反弹买入】条件单来实现
    ///
    /// 1. 没有最新买入单
    ///
    /// 2. 跌入valley谷底区间(当前K线最低价在谷底的最高价～最低价之间，排除跳空低开)，
    ///
    /// 3. 反弹2.0%, 在这个反弹Kline买入
    ///
    fn get_valley_rebound_buy(&self, kline: &Kline) -> Option<TradeStorder> {
        let mut result = None;
        //kline.high*0.97, 高点回撤3个点作为止损价表现貌似好点
        // #,security_id, profit_loss_ratio, win_rate,total_cross_profit,total_fee, total_trades
        // 0, SZ.STK.300868, 19.36, 72.73, 569.23, 21.09, 11

        //stop_price kline.low,
        // #,security_id, profit_loss_ratio, win_rate,total_cross_profit,total_fee, total_trades
        // 0, SZ.STK.300868, 2.86, 53.85, 298.23, 24.66, 13
        // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
        // 0, SZ.STK.002648, 15.70, 77.03, 1858.30, -118.40, 47.17, 74
        // 1, SZ.STK.300363, 15.63, 65.25, 16289.35, -1042.30, 434.30, 118
        // 2, SH.STK.603806, 10.30, 65.85, 4576.44, -444.16, 169.03, 82
        // 3, SH.STK.601555, 6.74, 61.54, 672.12, -99.74, 25.78, 26
        // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
        // 0, SZ.STK.300363, 23.63, 73.68, 13483.08, -570.56, 332.57, 57
        // 1, SH.STK.603806, 18.31, 73.68, 4427.07, -241.77, 143.20, 38
        // 2, SZ.STK.002648, 12.82, 75.00, 717.75, -56.00, 25.89, 16
        // 3, SH.STK.601555, 6.05, 77.78, 96.73, -16.00, 7.60, 9
        if let Some(valley) = &self.current_valley {
            if
                !self.is_last_buy() &&
                valley.is_support &&
                // kline.open > valley.lowest.low && // 排除跳空低开,这个反弹买入单没有设置，除非人工判断
                kline.low < valley.lowest.open && // 价格落入谷底，valley.lowest.high 作为反弹买入单的监控价格
                kline.is_rebound(2.0) //
            {
                // let st_price = round2(kline.low * 1.02); // 低位反弹2.0买入价，这个算出的st_price不准确，不能确保使用到未来的kline.low
                let st_price = round2(valley.lowest.open * 1.02); // 低位反弹2.0买入价，这个算出的st_price是最悲观的
                let st_volume = 100;
                let stop_price = if
                    kline.close > kline.open // 红盘
                {
                    round2(kline.low.max(kline.high * 0.97))
                } else {
                    kline.low
                }; //kline.low, 止损价
                let rebound_buy = ValleyReboundBuy {
                    valley_id: valley.id_raw(),
                    rebound_low: kline.low,
                    rebound_ratio: 2.0,
                    stop_price,
                    message: format!(
                        "based on Valley:{} stop_price {} ",
                        valley.id_raw(),
                        kline.low
                    ),
                };

                let kline_id = kline.id_raw();
                let stbuy_id = TradeStorder::id_from(kline_id.as_str());
                result = Some(TradeStorder {
                    id: Some(Thing::from(("trade_stbuy", stbuy_id.as_str()))),
                    security_id: self.security_id.clone(),
                    kline_id,
                    klts: kline.klts,

                    st_price,
                    st_volume,

                    data: Some(TradeStorderData::ValleyReboundBuy(rebound_buy)),
                });
            }
        }
        return result;
    }

    /// 如果有回撤了，买出止止损盈，使用【回落卖出】和【止盈止损】条件单来实现
    fn get_stop_loss_sell(&self, kline: &Kline) -> Option<(&TradeStorder, TradeStorder)> {
        let mut sell = None;

        // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
        // 0, SZ.STK.300363, 19.52, 70.18, 13350.69, -684.00, 332.30, 57
        // 1, SH.STK.603806, 12.25, 68.42, 4179.99, -341.36, 142.82, 38
        // 2, SZ.STK.002648, 7.30, 68.75, 591.29, -81.00, 25.73, 16
        // 3, SH.STK.601555, 2.92, 66.67, 87.58, -30.00, 7.59, 9
        // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
        // 0, SZ.STK.300363, 23.63, 73.68, 13483.08, -570.56, 332.57, 57
        // 1, SH.STK.603806, 18.31, 73.68, 4427.07, -241.77, 143.20, 38
        // 2, SZ.STK.002648, 12.82, 75.00, 717.75, -56.00, 25.89, 16
        // 3, SH.STK.601555, 6.05, 77.78, 96.73, -16.00, 7.60, 9

        if let (Some(current_valley), Some(to)) = (&self.current_valley, self.trade_orders.last()) {
            if kline.klts.signed_duration_since(to.klts).num_days() > 0 {
                // T+1 day
                match &to.data {
                    Some(TradeStorderData::ValleyReboundBuy(buy)) => {
                        if kline.low < buy.stop_price || kline.is_pulled_back(3.0) {
                            sell = Some(TradeStorder {
                                id: Some(Thing::from(("trade_storder", kline.id_raw().as_str()))),
                                security_id: kline.security_id.clone(),
                                kline_id: kline.id_raw(),
                                klts: kline.klts,
                                st_volume: 100,
                                st_price: buy.stop_price.max(kline.high * 0.97),
                                data: Some(
                                    TradeStorderData::PullbackSell(PullbackSell {
                                        valley_id: current_valley.id_raw(),
                                        buyorder_id: to.id_raw(),
                                        message: format!(
                                            "止盈信息,对应ValleyReboundBuy买单:{} ",
                                            to.id_raw()
                                        ),
                                    })
                                ),
                            });
                        }
                    }
                    Some(TradeStorderData::PullbackBuy(buy)) => {
                        if kline.low < buy.stop_price || kline.is_pulled_back(3.0) {
                            sell = Some(TradeStorder {
                                id: Some(Thing::from(("trade_storder", kline.id_raw().as_str()))),
                                security_id: kline.security_id.clone(),
                                kline_id: kline.id_raw(),
                                klts: kline.klts,
                                st_volume: 100,
                                st_price: buy.stop_price.max(kline.high * 0.97),
                                data: Some(
                                    TradeStorderData::PullbackSell(PullbackSell {
                                        valley_id: current_valley.id_raw(),
                                        buyorder_id: to.id_raw(),
                                        message: format!(
                                            "止盈信息,对应PullbackBuy买单:{} 止盈价:{}",
                                            to.id_raw(),
                                            buy.stop_price
                                        ),
                                    })
                                ),
                            });
                        }
                    }
                    _ => {}
                }
            }
        }

        if let (Some(last_to), Some(sl)) = (self.trade_orders.last(), sell) {
            Some((last_to, sl))
        } else {
            None
        }
    }

    /// 买出止盈后，再回撤到5日均线买回
    fn get_ready_for_pullback_buy(&self, kline: &Kline) -> Option<(&TradeStorder, TradeStorder)> {
        None
    }

    /// 移动止损
    fn get_moving_stop_price(&self, kline: &Kline) -> Option<f64> {
        let rs = if self.is_last_buy() {
            match self.trade_orders.last() {
                Some(to) => {
                    if kline.klts.signed_duration_since(to.klts).num_days() > 0 {
                        // T+1 day
                        let p1 = match &to.data {
                            Some(TradeStorderData::ValleyReboundBuy(buy)) => {
                                // 收盘价回撤3个点还大于止盈价就再更新止盈价，移动止盈
                                if kline.high * 0.97 > buy.stop_price {
                                    Some(round2(kline.high * 0.97))
                                } else {
                                    None
                                }
                            }
                            Some(TradeStorderData::PullbackBuy(buy)) => {
                                // 收盘价回撤3个点还大于止盈价就再更新止盈价，移动止盈
                                if kline.high * 0.97 > buy.stop_price {
                                    Some(round2(kline.high * 0.97))
                                } else {
                                    None
                                }
                            }
                            _ => None,
                        };
                        p1
                    } else {
                        None
                    }
                }
                None => None,
            }
        } else {
            None
        };

        rs
    }

    /// 查找支撑当前Kline的Valley
    pub async fn run_kline_valley_pairs(
        &mut self,
        kline_id_date: &str
    ) -> Option<Vec<KlineValleyPair>> {
        if
            let Some(end_kline) = self.all_klines
                .iter()
                .find(|kline| { kline.id_date().eq_ignore_ascii_case(kline_id_date) })
        {
            // let mut trade_orders: Vec<TradeStorder> = Vec::new();
            for kline in self.all_klines.iter() {
                if kline.klts <= end_kline.klts {
                    self.current_valley = self.get_current_valley(kline);

                    // 更新Valley成为阻力位、支撑位
                    for (idx, is_support) in self
                        .get_resistance_support_valley_flags(kline)
                        .iter() {
                        self.valleys[*idx].is_support = *is_support;
                    }
                }
            }

            let result: Vec<KlineValleyPair> = self.valleys
                .iter()
                .filter(|v| end_kline.klts > v.right_most.klts)
                .map(|v| KlineValleyPair { kline: end_kline.clone(), valley: v.clone() })
                .collect();

            if result.len() > 0 {
                return Some(result);
            }
        }

        None
    }
    //

    pub async fn run_find_latest_day_reverse_kline(&self) -> Option<Kline> {
        let mut result = None;
        for kline in self.all_klines.iter() {
            if kline.is_day_reverse(3.0, 4.0) {
                result = Some(kline.clone());
            }
        }
        result
    }
}

/// Kline ~ Valley 配对
pub struct KlineValleyPair {
    /// 当前Kline
    pub kline: Kline,

    /// 当前Valley
    pub valley: KlineValley,
}

impl KlineValleyPair {
    /// 所有者Security
    pub fn security_id(&self) -> &str {
        &self.kline.security_id
    }
    /// 当前Kline时间
    pub fn klts(&self) -> &DateTime<Utc> {
        &self.kline.klts
    }

    /// 当前Kline的ohlcv
    pub fn ohlcv(&self) -> Ohlcv {
        self.kline.ohlcv()
    }

    /// Valley的日期
    pub fn valley_date(&self) -> String {
        self.valley.lowest.id_date()
    }

    /// Valley到当前Kline的天数
    pub fn valley_days(&self) -> i64 {
        self.kline.klts.signed_duration_since(self.valley.lowest.klts).num_days()
    }

    /// Valley的ohlcv
    pub fn valley_ohlcv(&self) -> Ohlcv {
        self.valley.lowest.ohlcv()
    }

    /// 当前Kline.low与Valley.high比率，用于判断当前Kline与支撑位的距离
    /// 往1靠拢，表示越接近支撑位
    pub fn support_ratio(&self) -> f64 {
        round2(self.kline.low / self.valley.lowest.high)
    }

    /// 当前Kline.high与Valley.low比率,用于判断当前Kline与压力位的距离
    /// 往1靠拢，表示越接近阻力位
    pub fn resistance_ratio(&self) -> f64 {
        round2(self.kline.high / self.valley.lowest.low)
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use chrono::DateTime;
    use chrono::Duration;
    use chrono::Months;
    use chrono::Utc;
    use fudata::db;
    use fudata::model::kline::Klt;
    use fudata::model::security;
    use fudata::model::security::Market;
    use fudata::model::security::Security;

    use log::debug;
    use log::info;
    use log::warn;
    use log::error;
    use surrealdb::sql::Thing;
    use crate::qtrade::strategy::bottom_fishing::BottomFishingStrategyWorker;
    use crate::qtrade::strategy::get_strategy_test_securities;
    use crate::qtrade::strategy::print_strategy_performance_summary;
    use crate::qtrade::strategy::StrategyPerformance;
    use std::io::Write;
    use fudata::model::kline::Kline;
    use crate::qtrade::strategy::bottom_fishing::handle_inclusion;
    use crate::qtrade::strategy::bottom_fishing::find_bottom_fractal;

    use super::BottomFractal;

    #[test]
    fn test_logging() {
        // 设置 RUST_LOG 环境变量（仅在开发环境中设置）
        std::env::set_var("RUST_LOG", "info");
        // 自定义格式化器，去掉所有前缀信息
        pretty_env_logger
            ::formatted_timed_builder()
            .format_module_path(false) // 不显示模块路径
            .format_level(false) // 不显示日志级别
            .format_target(false) // 不显示目标
            .format(|buf, record| { buf.write_fmt(format_args!("{}", record.args())) })
            .init();
        // 测试日志记录
        debug!("\nThis is a debug message.");
        info!("\nThis is an println message.");
        warn!("\nThis is a warning message.");
        error!("\n***This is an error message.***\n");
    }

    #[tokio::test]
    async fn test_test_run() {
        let _ = db::init().await.unwrap();

        pretty_env_logger::init();

        let security_id = "SH.STK.601699";
        let date_part = "20220805";
        let rs = BottomFishingStrategyWorker::new(security_id, &Klt::DAY).await;

        match rs {
            Ok(mut worker) => {
                // if let Some(kline) = worker.find_kline(date_part) {
                worker.test_run(date_part);
                // }
            }

            Err(e) => {
                info!("err={:?}", e);
            }
        }
    }

    #[tokio::test]
    async fn test_run_buy_sell() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init();

        let securities = get_strategy_test_securities(
            1 == 1,
            Some(Market::SH),
            Some(security::SecurityType::STK),
            0,
            3000
        ).await;

        // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
        // 0, SZ.STK.300363, 2.09, 46.67, 116.78, -55.91, 4.61, 30
        // 1, SZ.STK.002648, 1.68, 22.22, 17.77, -10.60, 0.45, 18
        // 2, SH.STK.603806, 1.67, 38.46, 45.74, -27.43, 1.95, 26
        // 3, SH.STK.601555, 0.02, 10.00, 0.28, -14.48, 0.48, 10
        // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
        // 0, SZ.STK.002648, 15.70, 77.03, 1858.30, -118.40, 47.17, 74
        // 1, SZ.STK.300363, 15.63, 65.25, 16289.35, -1042.30, 434.30, 118
        // 2, SH.STK.603806, 10.30, 65.85, 4576.44, -444.16, 169.03, 82
        // 3, SH.STK.601555, 6.74, 61.54, 672.12, -99.74, 25.78, 26

        let mut sec_perfs = HashMap::new();
        for (idx, security) in securities.iter().enumerate() {
            let security_id = security.id_raw();
            let worker_no = format!("{idx}/{}", securities.len());
            let rs = BottomFishingStrategyWorker::new(security_id.as_str(), &Klt::DAY).await;

            match rs {
                Ok(mut worker) => {
                    worker.run_buy_sell().await;

                    info!("\n[{}] {}***************", worker_no, security_id);
                    for (idx, valley) in worker.valleys.iter().enumerate() {
                        info!(" . [{idx}] valley={} {} ", valley.id_raw(), valley.is_support);
                    }

                    sec_perfs.insert(security_id.clone(), worker.performances);

                    info!("[{}] {}***************\n", worker_no, security_id);
                }

                Err(e) => {
                    info!("err={:?}", e);
                }
            }
        }

        print_strategy_performance_summary(sec_perfs);
    }

    #[tokio::test]
    async fn test_run_find_latest_day_reverse_kline() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init();

        let securities = get_strategy_test_securities(
            1 == 1,
            Some(Market::SH),
            Some(security::SecurityType::STK),
            0,
            3000
        ).await;

        let mut sec_perfs = Vec::new();
        for (idx, security) in securities.iter().enumerate() {
            let security_id = security.id_raw();
            let worker_no = format!("{idx}/{}", securities.len());
            let rs = BottomFishingStrategyWorker::new(security_id.as_str(), &Klt::DAY).await;

            match rs {
                Ok(worker) => {
                    let rev_kline = worker.run_find_latest_day_reverse_kline().await;

                    if let Some(kl) = rev_kline {
                        sec_perfs.push(kl);
                    }
                    info!("[{}] {}***************\n", worker_no, security_id);
                }

                Err(e) => {
                    info!("err={:?}", e);
                }
            }
        }

        sec_perfs.sort_by(|a, b| a.klts.partial_cmp(&b.klts).unwrap_or(std::cmp::Ordering::Equal));
        println!("#, code, date, low");
        for (idx, kl) in sec_perfs.iter().enumerate() {
            println!("{idx}, {}, {}, {}", kl.security_id, kl.id_date(), kl.low);
        }
    }

    #[tokio::test]
    async fn test_run_kline_valley_pairs() {
        let _ = db::init().await.unwrap();

        pretty_env_logger::init();
        // 自定义格式化器，去掉所有前缀信息
        // pretty_env_logger
        //     ::formatted_timed_builder()
        //     .format_module_path(false) // 不显示模块路径
        //     .format_level(false) // 不显示日志级别
        //     .format_target(false) // 不显示目标
        //     .format(|buf, record| { buf.write_fmt(format_args!("{}", record.args())) })
        //     .init();
        // let mut all_buys = Vec::new();

        let securities: Vec<Security>;
        if 1 == 1 {
            let all_securities = db::security
                ::select(
                    &Some(Market::SZ),
                    Some(security::SecurityType::STK),
                    None,
                    Some("id ASC"),
                    0,
                    3000 //MAX_STK_SECURITY
                ).await
                .unwrap();
            // 使用迭代器过滤出 security_id 以 "SH.SZ.60" 开头的对象,如果您想要拥有所有权而非引用：
            securities = all_securities
                .into_iter()
                .filter(|security| {
                    let security_id = security.id_raw();
                    security_id.starts_with("SH.STK.60") ||
                        security_id.starts_with("SZ.STK.30") ||
                        security_id.starts_with("SZ.STK.00")
                })
                .collect();
        } else {
            securities = vec![Security {
                // id: Some(Thing::from(("security", "SZ.STK.300363"))),
                // id: Some(Thing::from(("security", "SZ.STK.002648"))),
                id: Some(Thing::from(("security", "SH.STK.605580"))),
                ..Default::default()
            }];
        }

        // #,security_id, profit_loss_ratio, win_rate, total_profit, total_loss, total_fee, total_trades
        // 0, SH.STK.605580, 99.99, 100.00, 2.00, 0.00, 2.29, 1

        let mut kline_valley_pairs = Vec::new();
        for (idx, security) in securities.iter().enumerate() {
            let security_id = security.id_raw();
            let worker_no = format!("{idx}/{}", securities.len());
            let rs = BottomFishingStrategyWorker::new(security_id.as_str(), &Klt::DAY).await;

            match rs {
                Ok(mut worker) => {
                    info!("[{}] {}***************\n", worker_no, security_id);

                    if let Some(kvpairs) = worker.run_kline_valley_pairs("20240923").await {
                        for kvp in kvpairs.into_iter() {
                            if kvp.valley_days() < 5 * 366 {
                                //5 年内的波谷
                                kline_valley_pairs.push(kvp);
                            }
                        }
                    }

                    info!("[{}] {}***************\n", worker_no, security_id);
                }

                Err(e) => {
                    info!("err={:?}", e);
                }
            }
        }

        kline_valley_pairs.sort_by(|a, b|
            b
                .support_ratio()
                .partial_cmp(&a.support_ratio())
                .unwrap_or(std::cmp::Ordering::Equal)
                .then_with(||
                    b
                        .valley_days()
                        .partial_cmp(&a.valley_days())
                        .unwrap_or(std::cmp::Ordering::Equal)
                )
        );

        println!(
            "\n#,security_id, valley_date, valley_days, is_support, support_ratio,support_memo,  is_resistance, resistance_ratio, resistance_memo"
        );
        for (idx, kvpair) in kline_valley_pairs.iter().enumerate() {
            println!(
                "{idx},{},{},{},{},{},{},{}",
                kvpair.security_id(),
                kvpair.valley_date(),
                kvpair.valley_days(),
                kvpair.valley.is_support,
                if kvpair.valley.is_support {
                    format!(
                        "{:.2}, L:{}/VH:{}",
                        kvpair.support_ratio(),
                        kvpair.ohlcv().l,
                        kvpair.valley_ohlcv().h
                    )
                } else {
                    format!("0,-")
                },
                !kvpair.valley.is_support,
                if !kvpair.valley.is_support {
                    format!(
                        "{:.2}, H:{}/VL:{}",
                        kvpair.resistance_ratio(),
                        kvpair.ohlcv().h,
                        kvpair.valley_ohlcv().l
                    )
                } else {
                    format!("0,-")
                }
            );
        }
    }

    #[tokio::test]
    async fn test_handle_inclusion() {
        let klines = vec![
            Kline::new("1", 110.0, 90.0), // 第1根K线
            Kline::new("2", 108.0, 92.0), // 包含在第1根K线中
            Kline::new("3", 112.0, 94.0), // 第3根K线
            Kline::new("4", 111.0, 95.0), // 包含在第3根K线中
            Kline::new("5", 113.0, 96.0), // 第5根K线
            Kline::new("6", 114.0, 97.0), // 第6根K线
            Kline::new("7", 115.0, 98.0), // 第7根K线
            Kline::new("8", 116.0, 99.0), // 第8根K线
            Kline::new("9", 117.0, 100.0), // 第9根K线
            Kline::new("10", 116.0, 99.0), // 第10根K线向下趋势开始
            Kline::new("11", 115.0, 100.0), // 包含在第10根K线中
            Kline::new("12", 114.0, 97.0), // 第12根K线
            Kline::new("13", 113.0, 98.0), // 包含在第12根K线中
            Kline::new("14", 112.0, 99.0), // 包含在第12、13根合并的K线中
            Kline::new("15", 200.0, 180.0), // 第15根K线, 包含在第16根K线
            Kline::new("16", 230.0, 160.0), // 第16根K线
            Kline::new("17", 200.0, 190.0) // 包含在第16根和15根合并的K线中
        ];

        let processed_klines = handle_inclusion(klines);

        for kline in processed_klines {
            println!("Kline: \"{}\", {}, {}", kline.id_raw(), kline.high, kline.low);
        }

        // ---- qtrade::strategy::bottom_fishing::test::test_handle_inclusion stdout ----
        // Kline: "1", 110, 92, Direction: Up
        // Kline: "3", 112, 95, Direction: Up
        // Kline: "5", 113, 96, Direction: Up
        // Kline: "6", 114, 97, Direction: Up
        // Kline: "7", 115, 98, Direction: Up
        // Kline: "8", 116, 99, Direction: Up
        // Kline: "9", 117, 100, Direction: Up
        // Kline: "10", 115, 99, Direction: Down
        // Kline: "12", 112, 97, Direction: Down
        // Kline: "15", 230, 190, Direction: Up
    }

    #[tokio::test]
    async fn test_find_bottom_fractal() {
        let klines = vec![
            Kline::new("1", 110.0, 92.0), // 第1根K线
            Kline::new("2", 112.0, 95.0), // 第2根K线
            Kline::new("3", 113.0, 96.0), // 第3根K线
            Kline::new("4", 111.0, 95.0), // 第4根K线
            Kline::new("5", 110.0, 94.0), // 第5根K线
            Kline::new("6", 114.0, 97.0), // 第6根K线
            Kline::new("7", 115.0, 98.0), // 第7根K线
            Kline::new("8", 116.0, 99.0), // 第8根K线
            Kline::new("9", 117.0, 100.0), // 第9根K线
            Kline::new("10", 116.0, 99.0), // 第10根K线向下趋势开始
            Kline::new("11", 115.0, 100.0), // 包含在第10根K线中
            Kline::new("12", 114.0, 97.0), // 第12根K线
            Kline::new("13", 113.0, 98.0), // 包含在第12根K线中
            Kline::new("14", 112.0, 99.0), // 包含在第12、13根合并的K线中
            Kline::new("15", 200.0, 180.0), // 第15根K线, 包含在第16根K线
            Kline::new("16", 230.0, 160.0), // 第16根K线
            Kline::new("17", 200.0, 190.0), // 包含在第16根和15根合并的K线中
            Kline::new("18", 210.0, 185.0), // 第18根K线
            Kline::new("19", 220.0, 190.0), // 第19根K线
            Kline::new("20", 215.0, 180.0) // 第20根K线
        ];

        let processed_klines = handle_inclusion(klines);

        for kline in processed_klines.iter() {
            println!("Processed Kline: \"{}\", {}, {}", kline.id_raw(), kline.high, kline.low);
        }
        println!("\n");
        let bottom_klines = find_bottom_fractal(&processed_klines, &Vec::new(), &Vec::new(), 1);

        for bf in bottom_klines {
            println!(
                "Bottom Fractal found Kline: \"{}\", {}, {}",
                bf.bottom.id_raw(),
                bf.bottom.high,
                bf.bottom.low
            );
        }
    }

    #[tokio::test]
    async fn test_find_securities_bottom_fractals() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init();
        // let sec_type = "ETF";
        let sec_type = "SZ.STK";
        let sec_type = "SH.STK";
        let securities = if sec_type.eq_ignore_ascii_case("ETF") {
            get_strategy_test_securities(
                1 == 2,
                None,
                Some(security::SecurityType::ETF),
                0,
                3000
            ).await
        } else if sec_type.eq_ignore_ascii_case("SZ.STK") {
            get_strategy_test_securities(
                1 == 2,
                Some(Market::SZ),
                Some(security::SecurityType::STK),
                0,
                3000
            ).await
        } else {
            get_strategy_test_securities(
                1 == 2,
                Some(Market::SH),
                Some(security::SecurityType::STK),
                0,
                3000
            ).await
        };

        println!("found {} {sec_type} securities", securities.len());

        let mut sec_perfs: Vec<BottomFractal> = Vec::new();
        for (idx, security) in securities.iter().enumerate() {
            let security_id = security.id_raw();
            let worker_no = format!("{idx}/{}", securities.len());
            let rs = BottomFishingStrategyWorker::new(security_id.as_str(), &Klt::DAY).await;

            match rs {
                Ok(worker) => {
                    let bf_kline = find_bottom_fractal(
                        &worker.all_klines,
                        &worker.crossovers,
                        &worker.crossunders,
                        1
                    );
                    for kl in bf_kline {
                        sec_perfs.push(kl);
                    }
                    info!("[{}] {}***************\n", worker_no, security_id);
                }

                Err(e) => {
                    info!("err={:?}", e);
                }
            }
        }

        sec_perfs = filter_bottom_fractals_within_x_months(sec_perfs, 3);

        sec_perfs.sort_by(|a, b|
            b.bottom.klts
                .partial_cmp(&a.bottom.klts)
                .unwrap_or(std::cmp::Ordering::Equal)
                .then_with(||
                    b.rlratio.partial_cmp(&a.rlratio).unwrap_or(std::cmp::Ordering::Equal)
                )
                .then_with(||
                    b.b2tratio.partial_cmp(&a.b2tratio).unwrap_or(std::cmp::Ordering::Equal)
                )
        );

        println!("#, code, l,b,r, bhigh, blow, r/l, reverse,ltop,b2tratio");
        // let mut prev_sec_id = "".to_string();
        for (idx, kl) in sec_perfs.iter().enumerate() {
            // if !kl.0.security_id.eq_ignore_ascii_case(prev_sec_id.as_str()) {
            //     println!("\n");
            // }
            println!(
                "{idx},{},{},{},{},{},{},{},{},{},{}",
                kl.left.security_id,
                kl.left.id_date(),
                kl.bottom.id_date(),
                kl.right.id_date(),
                kl.bottom.high,
                kl.bottom.low,
                kl.rlratio,
                kl.dayreverse,
                extract_last_from_string(&kl.topleft),
                kl.b2tratio
            );
            // prev_sec_id = kl.0.security_id.clone();
        }
    }

    fn filter_bottom_fractals_within_x_months(
        bfs: Vec<BottomFractal>,
        month: u32
    ) -> Vec<BottomFractal> {
        // 获取当前时间
        let now = Utc::now();

        // 计算6个月前的日期
        let x_months_ago = match now.naive_utc().checked_sub_months(Months::new(month)) {
            Some(date) => DateTime::<Utc>::from_utc(date, Utc),
            None => panic!("Failed to subtract 6 months from the current date"),
        };

        // 过滤 K 线，只保留最近6个月的数据
        bfs.into_iter()
            .filter(|bf| bf.bottom.klts >= x_months_ago)
            .collect()
    }

    ///   string input = "SH.STK.600669.DAY.20040212";
    fn extract_last_from_string(s: &str) -> String {
        let parts: Vec<&str> = s.split('.').collect();
        if parts.len() > 0 {
            parts[parts.len() - 1].to_string()
        } else {
            "N".to_string()
        }
    }

    #[tokio::test]
    async fn test_find_securities_sma5_above_sma13_summary() {
        let _ = db::init().await.unwrap();
        pretty_env_logger::init();
        // let sec_type = "ETF";
        let sec_type = "SZ.STK";
        let sec_type = "SH.STK";
        let securities = if sec_type.eq_ignore_ascii_case("ETF") {
            get_strategy_test_securities(
                1 == 2,
                None,
                Some(security::SecurityType::ETF),
                0,
                3000
            ).await
        } else if sec_type.eq_ignore_ascii_case("SZ.STK") {
            get_strategy_test_securities(
                1 == 2,
                Some(Market::SZ),
                Some(security::SecurityType::STK),
                0,
                3000
            ).await
        } else {
            get_strategy_test_securities(
                1 == 2,
                Some(Market::SH),
                Some(security::SecurityType::STK),
                0,
                3000
            ).await
        };

        println!("found {} {sec_type} securities", securities.len());

        let mut sec_perfs: Vec<(Kline, usize, String, String)> = Vec::new();
        let start_ = "20240800".to_string();
        for (idx, security) in securities.iter().enumerate() {
            let security_id = security.id_raw();
            let worker_no = format!("{idx}/{}", securities.len());
            let rs = BottomFishingStrategyWorker::new(security_id.as_str(), &Klt::DAY).await;

            match rs {
                Ok(worker) => {
                    if
                        let (Some(co), Some(cu)) = (
                            worker.crossovers.last(),
                            worker.crossunders.last(),
                        )
                    {
                        if co.1 > cu.1 && extract_last_from_string(&co.0) > start_ {
                            let days = worker.all_klines.len() - co.1;
                            sec_perfs.push((
                                worker.all_klines[co.1].clone(),
                                days,
                                co.0.clone(),
                                cu.0.clone(),
                            ));
                        }
                    }

                    info!("[{}] {}***************\n", worker_no, security_id);
                }

                Err(e) => {
                    info!("err={:?}", e);
                }
            }
        }

        sec_perfs.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));

        println!("#, code, date,high,low,days");
        // let mut prev_sec_id = "".to_string();
        for (idx, kl) in sec_perfs.iter().enumerate() {
            if idx > 10000 {
                break;
            }
            println!(
                "{idx},{},{},{},{},{}",
                extract_last_from_string(&kl.0.security_id),
                kl.0.id_date(),
                kl.0.high,
                kl.0.low,
                kl.1
                // extract_last_from_string(&kl.2),
                // extract_last_from_string(&kl.3)
            );
            // prev_sec_id = kl.0.security_id.clone();
        }
    }

    //
}
